package com.echat.serviceapigateway.security.service.impl;

import com.echat.commonlib.constant.ErrorCode;
import com.echat.commonlib.constant.ErrorCodeMsg;
import com.echat.commonlib.exception.exc.BussinessException;
import com.echat.commonlib.exception.exc.EchatSecurityException;
import com.echat.commonlib.exception.exc.ValidateFailedException;
import com.echat.commonlib.util.SpringContextUtil;
import com.echat.commonlib.util.StringUtil;
import com.echat.serviceapigateway.security.config.CacheConfig;
import com.echat.serviceapigateway.security.constant.Constants;
import com.echat.serviceapigateway.security.mapper.UserMapper;
import com.echat.serviceapigateway.security.model.authentication.User;
import com.echat.serviceapigateway.security.model.network.AccessIp;
import com.echat.serviceapigateway.security.service.AuthorizationService;
import com.echat.serviceapigateway.security.service.IpWhiteListService;
import com.echat.serviceapigateway.security.service.TokenGetLimiService;
import com.echat.serviceapigateway.security.service.UserService;
import com.echat.serviceapigateway.security.tool.EncryptUtils;
import com.echat.serviceapigateway.security.tool.SpringUtil;
import com.echat.serviceapigateway.security.tool.StringUtils;
import com.echat.serviceapigateway.security.util.IdGenerator;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import java.time.LocalDateTime;
import java.util.List;

/**
 * Created by chenmohan on 2018/08/30
 *
 * @description
 */
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;

    @Autowired
    IdGenerator idGenerator;

    @Autowired
    TokenGetLimiService tokenGetLimiService;

    @Autowired
    AuthorizationService authorizationService;

    @Autowired
    IpWhiteListService ipWhiteListService;

    @Transactional
    @Override
    public User createUser(User user) {
        //check null
        user.checkUserNameNotNull();

        LocalDateTime now = LocalDateTime.now();
        long userId = idGenerator.genId();
        User insertUser = User.builder()
                .id(userId)
                .userName(user.getUserName())
                .accessKey(EncryptUtils.uuid())
                .accessSecret(EncryptUtils.accessSecret(user.getUserName()))
                .signToken(EncryptUtils.signToken())
                .status(Constants.USER_ENABLE)
                .redirectUri(user.getRedirectUri())
                .createTime(now).modifyTime(now)
                //用户自行注册账号后，需要平台人员审计并将用户状态置为有效(1)，该用户才能正常访问接口 ;
                //如果是由管理员创建的用户，默认激活状态
                .status(user.getStatus())
                .build();
        try {
            userMapper.insertSelective(insertUser);
        } catch (DuplicateKeyException e) {
            throw new BussinessException("用户名重复");
        }

        //为新建用户创建 token获取次数上限
        tokenGetLimiService.initTokenGetLimit(userId);

        //为该用户分配初始权限(初始没有任何权限)
        authorizationService.addAuthorization(userId);

        //为该用户创建白名单
        ipWhiteListService.createAccessIp(userId);

        return insertUser;
    }

    @Override
    public User getUserByPrimaryKey(String userName) {
        User resultUser = userMapper.selectByPrimaryKey(userName);
        return resultUser;
    }

    @Cacheable(value = CacheConfig.CACHE_USERNAME, key = "#userName")
    @Override
    public String getAccessKeyByUserName(String userName) {
        User result = userMapper.selectByPrimaryKey(userName);
        if (result == null) {
            return null;
        }
        return result.getAccessKey();
    }

    @Override
    public boolean existUser(User user) {
        return userMapper.selectCount(user) > 0;
    }

    @Override
    public User getUser(User user) {
        return userMapper.selectOne(user);
    }

    /**
     * 根据accessKey查询指定用户
     *
     * @param accessKey
     * @return
     */
    @Cacheable(value = CacheConfig.CACHE_USER, key = "#accessKey")
    @Override
    public User getUserByAccessKey(String accessKey) {
        if (StringUtils.isEmpty(accessKey)) {
            throw new ValidateFailedException("accessKey不能为空");
        }
        //使用Example来查询对象
        Example example = new Example(User.class);
        example.createCriteria().andEqualTo("accessKey", accessKey);
        //设置用户的authCode
        User user = userMapper.selectOneByExample(example);
        return user;
    }

    /**
     * 根据accessKey查询指定用户id
     *
     * @param accessKey
     * @return 用户被禁用返回会抛出异常
     */
    @Cacheable(value = CacheConfig.CACHE_USER_ID, key = "#accessKey")
    @Override
    public String getUserIdByAccessKey(String accessKey) {
        User user = ((UserService) SpringContextUtil.getBean(this.getClass())).getUserByAccessKey(accessKey);
        //检查用户是否被禁用
        if (Constants.USER_UNENABLE.equals(user.getStatus())) {
            throw new EchatSecurityException(ErrorCode.ACCOUNT_IS_UNABLE, ErrorCodeMsg.ACCOUNT_IS_UNABLE);
        }
        return user.getId() + "";
    }

    @Override
    public void activeUser(Long userId) {
        User user = getUser(User.builder().id(userId).build());
        //如果已经激活过了，就直接返回
        if (user.getStatus().equals(Constants.USER_ENABLE)) {
            return;
        }
        //如果没有激活
        user.setStatus(Constants.USER_ENABLE);
        //SpringCache内部调用缓存方法B,方法B不会被动态代理,重新去SpringIOC中拿到增强对象
        ((UserService) SpringContextUtil.getBean(this.getClass())).activeUserByAccessKey(user);
        //激活了用户状态要重置认证鉴权缓存
        SpringUtil.clearUserAccessCahcer(userId);
    }

    @CachePut(value = CacheConfig.CACHE_USER, key = "#user.accessKey")
    @Override
    public User activeUserByAccessKey(User user) {
        userMapper.updateByPrimaryKeySelective(user);
        return user;
    }

    /**
     * 更新一些用户基本数据,比如accessSecret、signToken、用户状态
     * 这些数据热度不高,不需要缓存
     *
     * @param user
     * @return
     */
    @Override
    public User refresh(User user) {
        //必须传入主键userName
        if (user.getId() == null && StringUtil.isEmpty(user.getUserName())) {
            throw new ValidateFailedException("缺少用户相关的必要参数");
        }
        //先看是不是要更新 ip白名单
        if (user.getIpWhilteList() != null) {
            AccessIp update = AccessIp.builder()
                    .ips(user.getIpWhilteList().trim())
                    .userId(user.getId()).build();
            ipWhiteListService.updateAcceSSIp(update);
            return user;
        }
        String accessKeyByUserName = getAccessKeyByUserName(user.getUserName());
        user.setAccessKey(accessKeyByUserName);
        boolean needUpdateUser = false;
        //刷新accessSecret
        if (StringUtils.isNotEmpty(user.getAccessSecret())) {
            user.setAccessSecret(EncryptUtils.accessSecret());
            needUpdateUser = true;
        }
        //刷新signToken
        if (StringUtils.isNotEmpty(user.getSignToken())) {
            user.setSignToken(EncryptUtils.signToken());
            needUpdateUser = true;
        }
        if (user.getStatus() != null) {
            needUpdateUser = true;
        }
        if (needUpdateUser && StringUtils.isNotEmpty(accessKeyByUserName)) {
            userMapper.updateByPrimaryKeySelective(user);
        } else {
            throw new ValidateFailedException("缺少用户相关的必要参数");
        }
        return user;
    }

    @Override
    public PageInfo<User> listUsersByCondition(User condition) {
        Example example = new Example(User.class);
        Example.Criteria criteria = example.createCriteria();
        if (StringUtils.isNotEmpty(condition.getUserName())) {
            criteria.andLike("userName", condition.getUserName() + "%");
        }
        if (StringUtils.isNotEmpty(condition.getAccessKey())) {
            criteria.andEqualTo("accessKey", condition.getAccessKey());
        }
        if (condition.getStatus() != null) {
            criteria.andEqualTo("status", condition.getStatus());
        }
        //默认按照注册时间排序
        example.setOrderByClause("create_datetime DESC");

        PageHelper.startPage(condition.getPage(), condition.getLimit());
        List<User> userList = userMapper.selectByExample(example);
        PageInfo<User> userPageInfo = new PageInfo<>(userList);
        return userPageInfo;
    }
}
